/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 quest_site.cpp

	$Header: /heroes4/quest_site.cpp $

	$NoKeywords: $

 ************************************************************************/

#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "quest_site.h"

#include <cassert>

#include "abstract_script_action.h"
#include "abstract_script_expression.h"
#include "adv_border_guard.h"
#include "adv_object_type.h"
#include "adventure_frame.h"
#include "adventure_map.h"
#include "adventure_sounds.h"
#include "army.h"
#include "enum_operations.h"
#include "mouse_event.h"
#include "music.h"
#include "object_registration.h"
#include "player.h"
#include "script_sequence.h"
#include "script_simple_expression.h"
#include "simple_dialog.h"
#include "sized_int_types.h"
#include "streambuf_operators.h"

// --------------------------------------------------------------------------
// Unnamed namespace
// --------------------------------------------------------------------------

namespace
{

	namespace quest_origin_site_details
	{
		int const k_current_map_format_version = 1;
	} // quest_origin_site_details

	namespace quest_gate_details
	{
		int const k_current_map_format_version = 1;

		inline int to_base_map_format_version( int format_version )
		{
			if ( format_version < 1 )
				return 0;
			return 1;
		}
	} // quest_gate_details

	namespace quest_site_details
	{
		int const k_current_map_format_version = 2;

		inline int to_base_map_format_version( int format_version )
		{
			if ( format_version < 1 )
				return 0;
			return 1;
		}
	} // quest_site_details

	namespace quest_guard_details
	{
		int const k_current_map_format_version = 2;


		inline int to_base_map_format_version( int format_version )
		{
			if ( format_version < 1 )
				return 0;
			if ( format_version < 2 )
				return 1;
			return 2;
		}
	} // quest_guard_details

	namespace seers_hut_details
	{
		int const k_current_map_format_version = 2;

		inline int to_base_map_format_version( int format_version )
		{
			if ( format_version < 1 )
				return 0;
			if ( format_version < 2 )
				return 1;
			return 2;
		}
	} // seers_hut_details

	t_object_registration< t_quest_gate >	g_quest_gate_registration( k_adv_object_quest_gate );
	t_object_registration< t_quest_guard >	g_quest_guard_registration( k_adv_object_quest_guard );
	t_object_registration< t_seers_hut >	g_seers_hut_registration( k_adv_object_seers_hut );

} // Unnamed namespace


// --------------------------------------------------------------------------
// t_quest_origin_site members
// --------------------------------------------------------------------------

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
t_quest_origin_site::t_quest_origin_site(
	t_stationary_adventure_object const &	source )
	:	t_stationary_adventure_object( source ),
		m_completion_condition_ptr( new t_script_boolean_expression< k_script_boolean_false > ),
		m_triggered_script_ptr( new t_script_action< k_script_action_sequence > )
{
	m_visited_flags.reset();
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
t_quest_origin_site::t_condition const &
t_quest_origin_site::get_completion_condition() const
{
	assert( m_completion_condition_ptr.get() != 0 );
	return *m_completion_condition_ptr;
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
t_quest_origin_site::t_script const &
t_quest_origin_site::get_triggered_script() const
{
	assert( m_triggered_script_ptr.get() != 0 );
	return *m_triggered_script_ptr;
}

int const k_current_origin_version = 2;

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_quest_origin_site::read_version( std::streambuf &	stream,
										int					format_version,
										int *				script_version_ptr )
{
	assert( format_version >= 0 && format_version <= k_current_origin_version );
	if ( format_version < 0 || format_version > k_current_origin_version)
		return false;

	if ( format_version < 1 )
		return true;

	if (	!read_string16( stream, m_site_name )
		||	!read_string16( stream, m_quest_log_entry )
		||	!read_string16( stream, m_proposal_message )
		||	!read_string16( stream, m_progress_message ) )
		return false;

	int visited;
	int i;

	if ( format_version < 2)
		visited = 0;
	else
		visited = get<t_uint16>( stream );
	 
	m_visited_flags.reset();
	for (i = 0; i < k_active_player_color_count; ++i)
		if ((visited & (1 << i)) != 0)
			m_visited_flags.set( i );

	int script_version = get< t_uint16 >( stream );

	t_script_ptr new_triggered_script_ptr = t_script::reconstruct( stream, script_version );
	if ( new_triggered_script_ptr.get() == 0 )
		return false;
	m_triggered_script_ptr = new_triggered_script_ptr;

	t_condition_ptr new_completion_condition_ptr = t_condition::reconstruct( stream, script_version );
	if ( new_completion_condition_ptr.get() == 0 )
		return false;
	m_completion_condition_ptr = new_completion_condition_ptr;

	if ( script_version_ptr != 0 )
		*script_version_ptr = script_version;

	return true;
}

bool t_quest_origin_site::read_data( std::streambuf& stream, int* script_version_ptr )
{
	int version = get<t_int16>( stream );

	return read_version( stream, version, script_version_ptr );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_quest_origin_site::read_from_map_impl(
	std::streambuf &		stream,
	t_progress_handler *	handler,
	int						format_version )
{
	assert( format_version >= 0 && format_version <= quest_origin_site_details::k_current_map_format_version );

	if ( format_version < 1 )
		return true;

	if (	!read_string16( stream, m_site_name )
		||	!read_string16( stream, m_quest_log_entry )
		||	!read_string16( stream, m_proposal_message )
		||	!read_string16( stream, m_progress_message ) )
		return false;

	t_script_ptr new_triggered_script_ptr = t_script::reconstruct_from_map( stream );
	if ( new_triggered_script_ptr.get() == 0 )
		return false;
	m_triggered_script_ptr = new_triggered_script_ptr;

	t_condition_ptr new_completion_condition_ptr = t_condition::reconstruct_from_map( stream );
	if ( new_completion_condition_ptr.get() == 0 )
		return false;
	m_completion_condition_ptr = new_completion_condition_ptr;

	return true;
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_quest_origin_site::write_impl( std::streambuf & stream ) const
{
	put<t_int16>( stream, k_current_origin_version );
	if (	!write_string16( stream, m_site_name )
		||	!write_string16( stream, m_quest_log_entry )
		||	!write_string16( stream, m_proposal_message )
		||	!write_string16( stream, m_progress_message ) )
		return false;

	int visited = 0;
	int i;
	 
	for (i = 0; i < k_active_player_color_count; ++i)
		if (m_visited_flags[i])
			visited |= 1 << i;
	put<t_uint16>( stream, visited );

	put< t_uint16 >( stream, t_abstract_script_action::get_version() );

	return		m_triggered_script_ptr->write( stream )
			&&	m_completion_condition_ptr->write( stream );
}

void t_quest_origin_site::place( t_adventure_map& map, t_adv_map_point const& point )
{
	t_stationary_adventure_object::place( map, point );

	t_player_color	color;
	t_player*		player;

	for (color = t_player_color(0); color < k_active_player_color_count; enum_incr(color))
	{
		if (!m_visited_flags[color])
			continue;
		player = map.get_player( color );
		if (player == 0)
			continue;
		player->add( this );
	}
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void t_quest_origin_site::set_visited(t_player_color color, bool visited)
{
	assert( (color >= 0) && (color <= k_active_player_color_count) );
	m_visited_flags.set(color, visited);
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_quest_origin_site::get_visited(t_player_color color) const
{
	assert( (color >= 0) && (color <= k_active_player_color_count) );
	return m_visited_flags.test(color);
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void t_quest_origin_site::set_completed(t_player_color color, bool completed)
{
	assert( (color >= 0) && (color <= k_active_player_color_count) );
	m_completed_flags.set(color, completed);
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_quest_origin_site::get_completed(t_player_color color) const
{
	assert( (color >= 0) && (color <= k_active_player_color_count) );
	return m_completed_flags.test(color);
}

std::string	t_quest_origin_site::get_name() const
{
	return get_site_name();
}

void t_quest_origin_site::right_click( t_mouse_event const& event,
		                               t_adventure_frame* adventure_frame )
{
	t_player_color color = get_map()->get_player().get_color();
	t_skill_mastery	information = get_information_level();

	if (!m_visited_flags[color] && information < k_mastery_basic)
	{
		show_popup_text( get_text( "help" ), event.screen_point );
		return;
	}
	if (!m_completed_flags[color])
	{
		show_popup_text( get_quest_log_entry(), event.screen_point );
		return;
	}
	show_popup_text( get_text( "completed" ), event.screen_point );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void t_quest_origin_site::activate_trigger( t_army* army, t_adv_map_point const& point, 
										t_direction direction, t_adventure_frame* frame )
{
	t_counted_ptr<t_quest_origin_site> ref = this;

	assert(army);
	t_player* player = army->get_owner();
	assert(player);
	if ( player == NULL )
		return;

	t_player_color player_color = player->get_color();
	bool visited = get_visited( player_color );
	bool completed = get_completed( player_color );

	t_script_context_army context( 
							frame->get_map(), 
							&army->get_creatures(),
							army->get_owner(),
							NULL, NULL,
							&point );

	m_triggered_script_ptr->execute(context);
	if (m_triggered_script_ptr->pending_removal()) {
		m_triggered_script_ptr->clear_removal();
		m_triggered_script_ptr = new t_script_action< k_script_action_sequence >;
	}

	if( frame->get_map()->is_game_over() )
		return;

	if ( !visited ) {
		on_initial_visit( army, point, direction, frame );
		bool can_complete = 
			get_completion_condition().evaluate( context );

		if (can_complete) 
			on_complete( army, point, direction, frame );
	} else {
		if ( !completed ) {
			bool can_complete = 
				get_completion_condition().evaluate( context );

			if (can_complete) 
				on_complete( army, point, direction, frame );
			else
				on_revisited( army, point, direction, frame );
		} else
			on_completed( army, point, direction, frame );
	}
}

void t_quest_origin_site::on_initial_visit( t_army* army, t_level_map_point_2d point, 
											t_direction direction, t_adventure_frame* frame )
{
	std::string const& msg = get_proposal_message();
	if (!msg.empty() && !army->get_owner()->is_computer())
	{
		t_sound_cache music = get_music_playing();
		stop_music();
		get_dialog_sound( k_dialog_sound_quest )->play( get_sound_volume() );
		ok_dialog(msg);
		play_music( music );
	}
	set_visited( army->get_owner()->get_color() );
	
    t_player* player_ptr = army->get_owner();
    player_ptr->add( this );
}

void t_quest_origin_site::on_complete( t_army* army, t_level_map_point_2d point, 
											t_direction direction, t_adventure_frame* frame )
{
	set_completed( army->get_owner()->get_color() );

	army->get_owner()->inc_quests_completed();	// increment the number of quest completed by a player, for score screen

    
    t_player* player_ptr = army->get_owner();
    player_ptr->remove( this );
}

void t_quest_origin_site::on_completed( t_army* army, t_level_map_point_2d point, 
											t_direction direction, t_adventure_frame* frame )
{
}

void t_quest_origin_site::on_revisited( t_army* army, t_level_map_point_2d point, 
											t_direction direction, t_adventure_frame* frame )
{
	std::string const& msg = get_progress_message();
	if (!msg.empty() && !army->get_owner()->is_computer())
	{
		t_sound_cache music = get_music_playing();
		stop_music();
		get_dialog_sound( k_dialog_sound_quest )->play( get_sound_volume() );
		ok_dialog(msg);
		play_music( music );
	}
}

bool t_quest_origin_site::is_completed( t_creature_array const& army ) const
{
	if (army.get_owner() == NULL) 
		return false;

	t_expression_context_army context( 
							army.get_map(), 
							&army,
							army.get_owner(),
							NULL, NULL,
							&army.get_position() );
		
	return get_completion_condition().evaluate( context );
}

// --------------------------------------------------------------------------
// t_quest_gate members
// --------------------------------------------------------------------------

int const k_current_gate_version = 2;
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
int t_quest_gate::get_version() const
{
	return k_current_gate_version;
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_quest_gate::read( std::streambuf&					stream, 
						 t_qualified_adv_object_type const&	type,
						 int								version )
{
	assert( version >= 0 && version <= k_current_gate_version );

	if ( version < 1 )
		return read_version( stream, 0 );
	if ( version < 2 )
		return read_version( stream, 1 );
	return read_data( stream ); // no more stupid base-version conversion required.
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_quest_gate::read_from_map(
	std::streambuf &		stream,
	t_progress_handler *	handler )
{
	using namespace quest_gate_details;

	int format_version = get< t_uint16 >( stream );
	if ( format_version < 0 || format_version > k_current_map_format_version )
		return false;

	return	t_quest_origin_site::read_from_map_impl(
				stream,
				handler,
				to_base_map_format_version( format_version ) );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_quest_gate::write( std::streambuf & stream ) const
{
	return t_quest_origin_site::write_impl( stream );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_quest_gate::blocks_army( t_creature_array const& army, t_path_search_type path_type ) const
{
	// quest gates always block empty armies.  These armies are created as
	// part of the initialization for a split in army dialog.  Since we have no
	// idea what the final contents of the army will be, the safest course 
	// is to deny access to all empty armies.
	if (path_type == k_path_search_place_army)
		return true;
	if (army.empty() && path_type == k_path_search_standard)
		return true;
	return !is_completed(army);
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_quest_gate::is_triggered_by( t_creature_array const& army ) const
{
	return !is_completed(army);
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_quest_gate::trigger_event(t_army& army)
{
	t_counted_ptr<t_quest_gate> ref = this;

	t_player* player = army.get_owner();
	if ( player == NULL )
		return false;

	t_script_context_army context( 
							army.get_map(), 
							&army.get_creatures(),
							army.get_owner(),
							NULL, NULL,
							&army.get_position() );

	m_triggered_script_ptr->execute(context);
	if (m_triggered_script_ptr->pending_removal()) {
		m_triggered_script_ptr->clear_removal();
		m_triggered_script_ptr = new t_script_action< k_script_action_sequence >;
	}

	return true;
}


// --------------------------------------------------------------------------
// t_quest_site members
// --------------------------------------------------------------------------

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
t_quest_site::t_quest_site(
	t_stationary_adventure_object const &	source )
	:	t_quest_origin_site( source ),
		m_completion_script_ptr( new t_script_action< k_script_action_sequence > )
{
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
t_quest_site::t_script const & t_quest_site::get_completion_script() const
{
	assert( m_completion_script_ptr.get() != 0 );
	return *m_completion_script_ptr;
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_quest_site::read_completion_script(
	std::streambuf &	stream,
	int					script_version )
{
	t_script_ptr new_completion_script_ptr = t_script::reconstruct( stream, script_version );
	if ( new_completion_script_ptr.get() == 0 )
		return false;
	m_completion_script_ptr = new_completion_script_ptr;

	return true;
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_quest_site::read_completion_script_from_map( std::streambuf & stream )
{
	t_script_ptr new_completion_script_ptr = t_script::reconstruct_from_map( stream );
	if ( new_completion_script_ptr.get() == 0 )
		return false;
	m_completion_script_ptr = new_completion_script_ptr;

	return true;
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
int const k_current_site_version = 3;

bool t_quest_site::read_version( std::streambuf &	stream,
								 int				format_version,
								 int *				script_version_ptr )
{
	assert( format_version >= 0 && format_version <= k_current_site_version );
	if (format_version < 0 || format_version > k_current_site_version)
		return false;

	int script_version;

	if ( format_version < 1 )
	{
		if (!t_quest_origin_site::read_version( stream, 0, &script_version ))
			return false;
	} else if (format_version < 3)
	{
		if (!t_quest_origin_site::read_version( stream, 1, &script_version ))
			return false;
	}
	else if (!t_quest_origin_site::read_data( stream, &script_version ))
		return false; // no more version conversion required.

	if ( format_version < 1 )
		return true;

	if ( script_version_ptr != 0 )
		*script_version_ptr = script_version;

	return		read_string16( stream, m_completion_question )
			&&	( format_version < 2 || read_completion_script( stream, script_version ) );
}

bool t_quest_site::read_data( std::streambuf& stream, int* script_version )
{
	int version = get<t_int16>( stream );

	return read_version( stream, version, script_version );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_quest_site::read_from_map_impl(
	std::streambuf &		stream,
	t_progress_handler *	handler,
	int						format_version )
{
	using namespace quest_site_details;

	assert( format_version >= 0 && format_version <= k_current_map_format_version );

	if (	!t_quest_origin_site::read_from_map_impl(
				stream,
				handler,
				to_base_map_format_version( format_version ) ) )
		return false;

	if ( format_version < 1 )
		return true;

	return		read_string16( stream, m_completion_question )
			&&	( format_version < 2 || read_completion_script_from_map( stream ) );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_quest_site::write_impl( std::streambuf & stream ) const
{
	put<t_int16>( stream, k_current_site_version );
	return		t_quest_origin_site::write_impl( stream )
			&&	write_string16( stream, m_completion_question )
			&&	m_completion_script_ptr->write( stream );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void t_quest_site::on_complete( t_army* army, t_level_map_point_2d point, 
											t_direction direction, t_adventure_frame* frame )
{
	std::string const& msg = get_completion_question();
	
	if (!msg.empty() && !army->get_owner()->is_computer())
		if (!yes_no_dialog(msg)) 
			return;

	get_completion_script().execute( t_script_context_army( 
										army->get_map(), 
										&army->get_creatures(),
										army->get_owner(),
										NULL, NULL,
										&army->get_position() ) );

	if (get_completion_script().pending_removal())
		get_completion_script().clear_removal();

	if( army->get_map()->is_game_over() )
		return;

	t_quest_origin_site::on_complete(army, point, direction, frame);
}


// --------------------------------------------------------------------------
// t_quest_guard members
// --------------------------------------------------------------------------
int const k_current_guard_version = 3;

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
int t_quest_guard::get_version() const
{
	return k_current_guard_version;
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_quest_guard::read( std::streambuf&						stream, 
						  t_qualified_adv_object_type const&	type,
						  int									version )
{
	assert( version >= 0 && version <= k_current_guard_version );
	if (version < 3)
		return read_version( stream, version );
	return read_data( stream ); // no more silly version conversion
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_quest_guard::read_from_map(
	std::streambuf &		stream,
	t_progress_handler *	handler )
{
	using namespace quest_guard_details;

	int format_version = get< t_uint16 >( stream );
	if ( format_version < 0 || format_version > k_current_map_format_version )
		return false;

	return	t_quest_site::read_from_map_impl(
				stream,
				handler,
				to_base_map_format_version( format_version ) );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_quest_guard::write( std::streambuf & stream ) const
{
	return t_quest_site::write_impl( stream );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void t_quest_guard::on_complete( t_army* army, t_level_map_point_2d point, 
											t_direction direction, t_adventure_frame* frame )
{
	t_quest_site::on_complete(army, point, direction, frame);

	if ( get_completed( army->get_owner()->get_color() ) )
	{
		remove_chains( *this );
		frame->get_map()->remove_object( this );
	}
}


// --------------------------------------------------------------------------
// t_seers_hut members
// --------------------------------------------------------------------------

int const k_current_hut_version = 3;
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
int t_seers_hut::get_version() const
{
	return k_current_hut_version;
}

		inline int to_base_format_version( int format_version )
		{
			if ( format_version < 1 )
				return 0;
			if ( format_version < 2 )
				return 1;
			return 2;
		}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_seers_hut::read( std::streambuf&						stream, 
						t_qualified_adv_object_type const&	type,
						int									version )
{
	assert( version >= 0 && version <= k_current_hut_version );

	int script_version;

	if (version < 3)
	{
		if (!read_version( stream, version, &script_version ))
			return false;
	}
	else
		if (!read_data( stream, &script_version )) // no more version conversion
			return false;

	if ( version < 1 )
		return true;

	return		read_string16( stream, m_completed_message )
			&&	( version >= 2 
			   || t_quest_site::read_completion_script( stream, script_version ) );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_seers_hut::read_from_map(
	std::streambuf &		stream,
	t_progress_handler *	handler )
{
	using namespace seers_hut_details;

	int format_version = get< t_uint16 >( stream );
	if ( format_version < 0 || format_version > k_current_map_format_version )
		return false;

	if (	!t_quest_site::read_from_map_impl(
				stream,
				handler,
				to_base_map_format_version( format_version ) ) )
		return false;

	if ( format_version < 1 )
		return true;

	return		read_string16( stream, m_completed_message )
			&&	( format_version >= 2 || t_quest_site::read_completion_script_from_map( stream ) );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_seers_hut::write( std::streambuf & stream ) const
{
	return		t_quest_site::write_impl( stream )
			&&	write_string16( stream, m_completed_message );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void t_seers_hut::on_completed( t_army* army, t_level_map_point_2d point, 
											t_direction direction, t_adventure_frame* frame )
{
	if (!m_completed_message.empty() && !army->get_owner()->is_computer())
		ok_dialog(m_completed_message);
}

